﻿using DataModels.Models;
using GenericTools.CustomReporting;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace ReportingManagement.Components.Queries
{
    /// <summary>
    /// Interaction logic for QueryManagement.xaml
    /// </summary>
    public partial class QueryManagement : UserControl
    {
        public static event EventHandler<QueryManagementArgs> QueryOrCategoryModified;
        public event EventHandler<InteroperableUserControlEventArgs> StatusChanged;

        private User currentUser = null;
        private ObservableCollection<QueryCategory> queryCategories = null;
        private ObservableCollection<Query> queries = null;
        private BackgroundWorker backgroundWorker = null;

        private IQueryDesigner queryDesigner;

        public QueryManagement()
        {
            InitializeComponent();
            btnModify.IsEnabled = (queryDesigner != null);
        }

        public IQueryDesigner QueryDesigner 
        {
            get { return queryDesigner; }
            set
            {
                queryDesigner = value;
                btnModify.IsEnabled = (queryDesigner != null);
            }
        }

        void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                if (e.Result != null)
                {
                    Query query = e.Result as Query;
                    if (query != null)
                    {
                        // Notifying clients about the changes
                        if (QueryManagement.QueryOrCategoryModified != null)
                        {
                            var args = new QueryManagementArgs
                            {
                                ActionPerformed = QueryManagementArgs.Action.QUERY_UPDATED,
                                Target = query
                            };
                            QueryManagement.QueryOrCategoryModified(this, args);
                        }
                    }
                    else
                    {
                        string message = e.Result as string;
                        MessageBox.Show(message, GenericTools.AppConfiguration.ApplicationName,
                                        MessageBoxButton.OK, MessageBoxImage.Exclamation);
                    }
                }

                var interopArgs = new InteroperableUserControlEventArgs
                {
                    StatusText = "Ready",
                    Type = InteroperableUserControlEventArgs.StatusType.Text
                };
                if (StatusChanged != null) StatusChanged(this, interopArgs);
            }
            else
            {
                // An error occured so we're reporting it as it is.
                MessageBox.Show(e.Error.Message, GenericTools.AppConfiguration.ApplicationName,
                                MessageBoxButton.OK, MessageBoxImage.Error);

                var args = new InteroperableUserControlEventArgs
                {
                    StatusText = e.Error.Message,
                    Type = InteroperableUserControlEventArgs.StatusType.Text
                };
                if (StatusChanged != null) StatusChanged(this, args);
            }

            btnRefreshQueries.Content = "Refresh";
            btnSave.Content = "Save";
            btnDelete.Content = "Delete";
            btnRefreshQueries.IsEnabled = true;
            btnSave.IsEnabled = true;
            btnDelete.IsEnabled = true;
        }

        void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            Query query = e.Argument as Query;
            if (query == null)
            {
                // We are going to refresh the list of query and its categories to reflect the changes
                // we have made.
                var categories = ReportQuerying.GetAllQueryCategories();
                queryCategories = new ObservableCollection<QueryCategory>(categories);
                var reportQueries = ReportQuerying.GetAllQueries();
                queries = new ObservableCollection<Query>(reportQueries);

                Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate()
                {
                    lstQueryCategories.ItemsSource = queryCategories;
                    lstReportQueries.ItemsSource = queries;
                });
            }
            else
            {            
                int status = ReportQuerying.UpdateQuery(query, query.QueryParameters);
                if (status == ReportQuerying.STATUS_OK_TO_CONTINUE)
                {
                    // Here we will deal with query assignment to categories
                    int queryId = query.Id;
                    List<int> categoryIds = new List<int>();
                    
                    Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate()
                    {
                        var checkboxes = GetAllEmbededCheckboxes();
                        for (int i = 0; i < checkboxes.Count; i++)
                        {
                            var checkbox = checkboxes[i];
                            if (checkbox.IsChecked.HasValue && checkbox.IsChecked.Value)
                            {
                                QueryCategory category = lstQueryCategories.Items[i] as QueryCategory;
                                if (category != null) categoryIds.Add(category.Id);
                            }
                        }
                    });                    

                    status = ReportQuerying.AddQueryIntoCategories(queryId, categoryIds, false);
                    if (status == ReportQuerying.STATUS_OK_TO_CONTINUE)
                    {
                        // We are going to refresh the list of query and its categories to reflect the changes
                        // we have made.
                        var categories = ReportQuerying.GetAllQueryCategories();
                        queryCategories = new ObservableCollection<QueryCategory>(categories);
                        var reportQueries = ReportQuerying.GetAllQueries();
                        queries = new ObservableCollection<Query>(reportQueries);
                        
                        Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate()
                        {
                            lstQueryCategories.ItemsSource = queryCategories;
                            lstReportQueries.ItemsSource = queries;
                        });
                        return; // Everything is OK
                    }
                    else
                    {
                        string message = "Unknown error occured that prevents you from saving your changes";
                        switch (status)
                        {
                            case ReportQuerying.STATUS_OBJECT_DOES_NOT_EXISTS:
                                message = "Selected query category does not exists";
                                break;

                            case ReportQuerying.STATUS_OBJECT_HAS_RELATED_CHILDREN:
                                message = "Selected query category has more than one query associated with it";
                                break;
                        }

                        e.Result = message;
                    }
                }
                else
                {
                    string message = "Unknown error occured that prevents you from saving your changes";
                    switch (status)
                    {
                        case ReportQuerying.STATUS_OBJECT_DOES_NOT_EXISTS:
                            message = "Selected query category does not exists";
                            break;

                        case ReportQuerying.STATUS_OBJECT_HAS_RELATED_CHILDREN:
                            message = "Selected query category has more than one query associated with it";
                            break;
                    }
                    e.Result = message;
                }
            }
        }

        void backgrondWorker_DoWork_DeleteQuery(object sender, DoWorkEventArgs e)
        {
            Query query = e.Argument as Query;
            if (query != null)
            {
                int status = ReportQuerying.DeleteQuery(query);
                if (status == ReportQuerying.STATUS_OK_TO_CONTINUE)
                {
                    // We are going to refresh the list of query and its categories to reflect the changes
                    // we have made.
                    var categories = ReportQuerying.GetAllQueryCategories();
                    queryCategories = new ObservableCollection<QueryCategory>(categories);
                    var reportQueries = ReportQuerying.GetAllQueries();
                    queries = new ObservableCollection<Query>(reportQueries);

                    Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate()
                    {
                        lstQueryCategories.ItemsSource = queryCategories;
                        lstReportQueries.ItemsSource = queries;
                    });
                }
                else
                {
                    string message = "Unknown error occured that prevents you from saving your changes";
                    switch (status)
                    {
                        case ReportQuerying.STATUS_OBJECT_DOES_NOT_EXISTS:
                            message = "Selected query does not exist in the database. It may have already been deleted.";
                            break;

                        case ReportQuerying.STATUS_OBJECT_HAS_RELATED_CHILDREN:
                            message = "Selected query is associated with at least one category so it can not be deleted";
                            break;
                    }
                    e.Result = message;
                }
            }
        }

        public void GetReportQueriesAndCategories(int userId)
        {
            currentUser = ReportQuerying.GetUserProfile(userId);
            var categories = ReportQuerying.GetAllQueryCategories();
            queryCategories = new ObservableCollection<QueryCategory>(categories);
            lstQueryCategories.ItemsSource = queryCategories;

            var reportQueries = ReportQuerying.GetAllQueries();
            queries = new ObservableCollection<Query>(reportQueries);
            lstReportQueries.ItemsSource = queries;
        }

        private void Hyperlink_Click(object sender, RoutedEventArgs e)
        {
            Hyperlink hyperLink = (Hyperlink)e.OriginalSource;
            QueryCategory category = null;
            string tag = hyperLink.Tag as string;
            switch (tag)
            {
                case "ADD":
                    if (currentUser != null)
                    {
                        category = new QueryCategory
                        {
                            Id = 0,
                            CreatedByUserId = currentUser.ID
                        };

                        var editor = new QueryCategoryEditor(category);
                        // Let's try to find the parent of this windows, so that when is shown it always be
                        // the modal dialog
                        Window mainWindowWpf = Window.GetWindow(this);
                        if (mainWindowWpf != null) editor.Owner = mainWindowWpf;
                        else
                        {
                            var mainWindowFormHandle = MainWindow.GetActiveWindow();
                            var helper = new System.Windows.Interop.WindowInteropHelper(editor);
                            helper.Owner = mainWindowFormHandle;
                        }

                        bool? okToContinue = editor.ShowDialog();
                        if (okToContinue.HasValue && okToContinue.Value)
                        {
                            var item = editor.QueryCategory;
                            queryCategories.Add(item);
                            // Notifying clients about the changes
                            if (QueryManagement.QueryOrCategoryModified != null)
                            {
                                var args = new QueryManagementArgs
                                {
                                    ActionPerformed = QueryManagementArgs.Action.CATEGORY_ADDED,
                                    Target = item
                                };
                                QueryManagement.QueryOrCategoryModified(this, args);
                            }
                            return;
                        }
                    }
                    break;

                case "EDIT":
                    category = lstQueryCategories.SelectedItem as QueryCategory;
                    if (category != null)
                    {
                        if (currentUser != null) category.UpdatedByUserId = currentUser.ID;
                        var editor = new QueryCategoryEditor(category);
                        // Let's try to find the parent of this windows, so that when is shown it always be
                        // the modal dialog
                        Window mainWindowWpf = Window.GetWindow(this);
                        if (mainWindowWpf != null) editor.Owner = mainWindowWpf;
                        else
                        {
                            var mainWindowFormHandle = MainWindow.GetActiveWindow();
                            var helper = new System.Windows.Interop.WindowInteropHelper(editor);
                            helper.Owner = mainWindowFormHandle;
                        }

                        bool? okToContinue = editor.ShowDialog();
                        if (okToContinue.HasValue && okToContinue.Value)
                        {
                            // Notifying clients about the changes
                            if (QueryManagement.QueryOrCategoryModified != null)
                            {
                                var args = new QueryManagementArgs
                                {
                                    ActionPerformed = QueryManagementArgs.Action.CATEGORY_UPDATED,
                                    Target = category
                                };
                                QueryManagement.QueryOrCategoryModified(this, args);
                            }
                        }
                    }
                    break;

                case "DELETE":
                    category = lstQueryCategories.SelectedItem as QueryCategory;
                    if (category != null)
                    {
                        if (category.Queries.Count == 0)
                        {
                            try
                            {
                                int status = ReportQuerying.DeleteQueryCategory(category.Id);
                                if (status == ReportQuerying.STATUS_OK_TO_CONTINUE)
                                {
                                    queryCategories.Remove(category);
                                    // Notifying clients about the changes
                                    if (QueryManagement.QueryOrCategoryModified != null)
                                    {
                                        var args = new QueryManagementArgs
                                        {
                                            ActionPerformed = QueryManagementArgs.Action.CATEGORY_DELETED,
                                            Target = category
                                        };
                                        QueryManagement.QueryOrCategoryModified(this, args);
                                    }
                                }
                                else
                                {
                                    string message = "Unknown error occured that prevents you from saving your changes";
                                    switch (status)
                                    {
                                        case ReportQuerying.STATUS_OBJECT_DOES_NOT_EXISTS:
                                            message = "Selected query category does not exists";
                                            break;

                                        case ReportQuerying.STATUS_OBJECT_HAS_RELATED_CHILDREN:
                                            message = "Selected query category has more than one query associated with it";
                                            break;
                                    }

                                    MessageBox.Show(message, GenericTools.AppConfiguration.ApplicationName,
                                        MessageBoxButton.OK, MessageBoxImage.Exclamation);
                                }
                            }
                            catch (Exception ex)
                            {
                                MessageBox.Show(ex.Message, GenericTools.AppConfiguration.ApplicationName, 
                                    MessageBoxButton.OK, MessageBoxImage.Error);
                            }
                        }
                        else
                        {
                            MessageBox.Show("Query category has related queries", 
                                GenericTools.AppConfiguration.ApplicationName, MessageBoxButton.OK, 
                                MessageBoxImage.Stop);
                        }
                    }
                    break;

                case "EDIT_PARAMETERS":
                    var query = lstReportQueries.SelectedItem as Query;
                    if (query != null && (query.QueryParameters.Count > 0))
                    {
                        if (currentUser != null) query.UpdatedByUserId = currentUser.ID;
                        var editor = new QueryParametersCreator(query.QueryParameters);

                        // Let's try to find the parent of this windows, so that when is shown it always be
                        // the modal dialog
                        Window mainWindowWpf = Window.GetWindow(this);
                        if (mainWindowWpf != null) editor.Owner = mainWindowWpf;
                        else
                        {
                            var mainWindowFormHandle = MainWindow.GetActiveWindow();
                            var helper = new System.Windows.Interop.WindowInteropHelper(editor);
                            helper.Owner = mainWindowFormHandle;
                        }

                        bool? okToContinue = editor.ShowDialog();
                        if (okToContinue.HasValue && okToContinue.Value)
                        {
                            int status = ReportQuerying.UpdateQuery(query, editor.QueryParameters);
                            if (status == ReportQuerying.STATUS_OK_TO_CONTINUE)
                            {
                                // Notifying clients about the changes
                                if (QueryManagement.QueryOrCategoryModified != null)
                                {
                                    var args = new QueryManagementArgs
                                    {
                                        ActionPerformed = QueryManagementArgs.Action.QUERY_UPDATED,
                                        Target = query
                                    };
                                    QueryManagement.QueryOrCategoryModified(this, args);
                                }
                                return;
                            }
                            else
                            {
                                string message = "Unknown error occured that prevents you from saving your changes";
                                switch (status)
                                {
                                    case ReportQuerying.STATUS_OBJECT_DOES_NOT_EXISTS:
                                        message = "Selected query category does not exists";
                                        break;

                                    case ReportQuerying.STATUS_OBJECT_HAS_RELATED_CHILDREN:
                                        message = "Selected query category has more than one query associated with it";
                                        break;
                                }

                                MessageBox.Show(message, GenericTools.AppConfiguration.ApplicationName,
                                    MessageBoxButton.OK, MessageBoxImage.Exclamation);
                            }
                        }
                    }
                    break;
              
                case "RUN":
                    category = lstQueryCategories.SelectedItem as QueryCategory;
                    if (category != null)
                    {
                        var queryResult = new QueryResults(category);
                        // Let's try to find the parent of this windows, so that when is shown it always be
                        // the modal dialog
                        Window mainWindowWpf = Window.GetWindow(this);
                        if (mainWindowWpf != null) queryResult.Owner = mainWindowWpf;
                        else
                        {
                            var mainWindowFormHandle = MainWindow.GetActiveWindow();
                            var helper = new System.Windows.Interop.WindowInteropHelper(queryResult);
                            helper.Owner = mainWindowFormHandle;
                        }

                        queryResult.ShowDialog();
                    }
                    break;
            }            
        }
        
        private void lstReportQueries_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            Query targetQuery = lstReportQueries.SelectedItem as Query;
            if (targetQuery != null)
            {
                // Clear all previously checked categories
                var checkboxes = GetAllEmbededCheckboxes();
                foreach (var checkbox in checkboxes) if (checkbox != null) checkbox.IsChecked = false;
                
                // Now we are going to check through all categories and check the one that matches
                // the target query.
                for (int i = 0; i < lstQueryCategories.Items.Count; i++)
                {
                    bool foundQuery = false;
                    QueryCategory category = lstQueryCategories.Items[i] as QueryCategory;
                    if (category != null)
                    {
                        foreach (var query in category.Queries)
                        {
                            if (query.Id == targetQuery.Id)
                            {
                                CheckBox categoryCheckbox = checkboxes[i];
                                categoryCheckbox.IsChecked = true;
                                foundQuery = true;
                                break; ;
                            }
                        }

                        if (foundQuery) continue;
                    }
                }
            }
        }

        private List<CheckBox> GetAllEmbededCheckboxes()
        {
            var checkboxes = new List<CheckBox>();
            for (int i = 0; i < lstQueryCategories.Items.Count; i++)
            {
                ListBoxItem item = lstQueryCategories.ItemContainerGenerator.ContainerFromIndex(i) as ListBoxItem;
                if (item == null) checkboxes.Add(null);
                else
                {
                    ContentPresenter contentPresenter = QueryParametersCreator.FindVisualChild<ContentPresenter>(item);
                    DataTemplate dataTemplate = contentPresenter.ContentTemplate;

                    CheckBox checkBox = (CheckBox)dataTemplate.FindName("cboQuery", contentPresenter);
                    checkboxes.Add(checkBox);
                }
            }

            return checkboxes;
        }

        private void btnSave_Click(object sender, RoutedEventArgs e)
        {
            Query query = lstReportQueries.SelectedItem as Query;
            if (query != null)
            {
                if (currentUser != null) query.UpdatedByUserId = currentUser.ID;
                btnRefreshQueries.IsEnabled = false;
                btnDelete.IsEnabled = false;
                btnSave.IsEnabled = false;
                btnSave.Content = "Please wait...";

                backgroundWorker = new BackgroundWorker();
                backgroundWorker.DoWork += backgroundWorker_DoWork;
                backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
                backgroundWorker.RunWorkerAsync(query);
                var args = new InteroperableUserControlEventArgs
                {
                    StatusText = "Please wait...",
                    Type = InteroperableUserControlEventArgs.StatusType.Text
                };
                if (StatusChanged != null) StatusChanged(this, args);
            }
        }

        public static void FireEvent(QueryManagementArgs args)
        {
            if (QueryOrCategoryModified != null) QueryOrCategoryModified(null, args);
        }

        private void btnRefreshQueries_Click(object sender, RoutedEventArgs e)
        {
            btnRefreshQueries.IsEnabled = false;
            btnSave.IsEnabled = false;
            btnDelete.IsEnabled = false;
            btnRefreshQueries.Content = "Please wait...";

            backgroundWorker = new BackgroundWorker();
            backgroundWorker.DoWork += backgroundWorker_DoWork;
            backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
            backgroundWorker.RunWorkerAsync();
            var args = new InteroperableUserControlEventArgs
            {
                StatusText = "Please wait...",
                Type = InteroperableUserControlEventArgs.StatusType.Text
            };
            if (StatusChanged != null) StatusChanged(this, args);
        }

        private void btnDelete_Click(object sender, RoutedEventArgs e)
        {
            Query query = lstReportQueries.SelectedItem as Query;
            if (query != null)
            {
                var response = MessageBox.Show(
                    string.Format("Are you sure you want to delete query: {0}?", query.Name),
                    GenericTools.AppConfiguration.ApplicationName, MessageBoxButton.YesNo, MessageBoxImage.Question);
                if (response == MessageBoxResult.Yes)
                {
                    query.DeletedByUserId = currentUser.ID;
                    btnRefreshQueries.IsEnabled = false;
                    btnSave.IsEnabled = false;
                    btnDelete.IsEnabled = false;
                    btnDelete.Content = "Please wait...";

                    backgroundWorker = new BackgroundWorker();
                    backgroundWorker.DoWork += backgrondWorker_DoWork_DeleteQuery;
                    backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
                    backgroundWorker.RunWorkerAsync(query);
                    var args = new InteroperableUserControlEventArgs
                    {
                        StatusText = "Please wait...",
                        Type = InteroperableUserControlEventArgs.StatusType.Text
                    };
                    if (StatusChanged != null) StatusChanged(this, args);
                }
            }
        }

        private void btnRun_Click(object sender, RoutedEventArgs e)
        {
            Query query = lstReportQueries.SelectedItem as Query;
            if (query != null)
            {
                var queryResult = new QueryResults(query);

                // Let's try to find the parent of this windows, so that when is shown it always be
                // the modal dialog
                Window mainWindowWpf = Window.GetWindow(this);
                if (mainWindowWpf != null) queryResult.Owner = mainWindowWpf;
                else
                {
                    var mainWindowFormHandle = MainWindow.GetActiveWindow();
                    var helper = new System.Windows.Interop.WindowInteropHelper(queryResult);
                    helper.Owner = mainWindowFormHandle;
                }

                queryResult.ShowDialog();
            }
        }

        private void btnModify_Click(object sender, RoutedEventArgs e)
        {
            if (queryDesigner != null)
            {
                Query query = lstReportQueries.SelectedItem as Query;
                if (query != null) queryDesigner.ShowInDesigner(query.Definition);
            }
        }
    }

    public class QueryManagementArgs : EventArgs
    {
        public enum Action
        {
            CATEGORY_ADDED, CATEGORY_UPDATED, CATEGORY_DELETED,
            QUERY_ADDED, QUERY_UPDATED, QUERY_DELETED
        };

        public Action ActionPerformed { get; set; }
        public object Target { get; set; }
    }
}
